package keye.document_manage.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import keye.document_manage.annotation.PushLog;
import keye.document_manage.dao.sqlite.BaseDaoSqlite;
import keye.document_manage.dao.sqlserver.BaseDaoSqlserver;
import keye.document_manage.dao.sqlserver.IArchivenoFormatDao;
import keye.document_manage.dao.sqlserver.IClassEntityRelationDao;
import keye.document_manage.dao.sqlserver.IsFilesDao;
import keye.document_manage.dao.sqlserver.TableInterfaceListViewDao;
import keye.document_manage.domain.ClassEntityRelation;
import keye.document_manage.domain.ClassEntityRelationWithArchive;
import keye.document_manage.entity.Page;
import keye.document_manage.entity.SelectParameters;
import keye.document_manage.entity.TableFiled;
import keye.document_manage.gui.CheckNode;
import keye.document_manage.gui.ProgressFrame;
import keye.document_manage.service.IPushService;
import keye.document_manage.util.FileUtil;
import keye.document_manage.util.MultipleDataSource;
import keye.document_manage.util.MyConstants;
import keye.document_manage.util.MySqlProvider;

@Service("pushService")
public class PushServiceImpl implements IPushService {

	@Resource
	private BaseDaoSqlserver<?> baseDao;
	@Resource(name = "sqliteBaseDao")
	private BaseDaoSqlite<?> baseDaoSqlite;
	@Resource
	private IClassEntityRelationDao classEntityRelationDao;
	@Resource
	private TableInterfaceListViewDao listViewDao;
	@Resource
	private IArchivenoFormatDao formatDao;
	@Resource
	private IsFilesDao filesDao;

	public void setBaseDaoSqlite(BaseDaoSqlite<?> baseDaoSqlite) {
		this.baseDaoSqlite = baseDaoSqlite;
	}

	public void setBaseDao(BaseDaoSqlserver<?> baseDao) {
		this.baseDao = baseDao;
	}

	public IClassEntityRelationDao getClassEntityRelationDao() {
		return classEntityRelationDao;
	}

	public void setClassEntityRelationDao(
			IClassEntityRelationDao classEntityRelationDao) {
		this.classEntityRelationDao = classEntityRelationDao;
	}

	public TableInterfaceListViewDao getListViewDao() {
		return listViewDao;
	}

	public void setListViewDao(TableInterfaceListViewDao listViewDao) {
		this.listViewDao = listViewDao;
	}

	public IArchivenoFormatDao getFormatDao() {
		return formatDao;
	}

	public void setFormatDao(IArchivenoFormatDao formatDao) {
		this.formatDao = formatDao;
	}

	public IsFilesDao getFilesDao() {
		return filesDao;
	}

	public void setFilesDao(IsFilesDao filesDao) {
		this.filesDao = filesDao;
	}

	// cdg 2016-2-18 14:55:05
	@Override
	public Double calculateSize(List<SelectParameters> selectParas,
			CheckNode checkNode) {
		ClassEntityRelation cer = (ClassEntityRelation) checkNode
				.getUserObject();
		// 被选节点没有相关表信息
		if (cer.geteTableName() == null || "".equals(cer.geteTableName()))
			return -1.0;
		// 不是叶子节点，即为多层结构，若是叶子节点，则不用关心是否是多层结构
		if (checkNode.isLeaf()) {
			return filesDao.getFilesTotalSize(selectParas, cer.geteTableName());
		} else {
			checkNode = (CheckNode) checkNode.getFirstLeaf();
			return filesDao.getFilesTotalSize(selectParas,
					((ClassEntityRelation) checkNode.getUserObject())
							.geteTableName(), cer.geteTableName());
		}
	}

	protected boolean pushDataOne(ProgressFrame progressFrame,
			ClassEntityRelationWithArchive entityRelation,
			List<SelectParameters> selectParas, String ceType,
			boolean isCopyFiles, String osPosition, String newPath) {
		List<HashMap<String, Object>> values = baseDao.getAllWithParas(
				entityRelation.geteTableName(), selectParas);
		// 复制表结构和数据
		copyTable(entityRelation.geteTableName(), values);
		// 复制相关系统表
		copySysTable(entityRelation, "'" + ceType + "'",null);
		if (isCopyFiles) {
			copyIsFilesTableAndFiles(progressFrame,
					getRceidAndRule(values, entityRelation.getFormat()[2]),
					osPosition, newPath, selectParas,
					entityRelation.geteTableName());
		}
		return true;
	}

	/**
	 * 复制系统表 单层结构
	 * 
	 * @param entityRelation
	 * @return
	 */
	public boolean copySysTable(ClassEntityRelation entityRelation) {

		return copySysTable(entityRelation, null,null);
	}

	/**
	 * 复制系统表 
	 * 
	 * @param entityRelation
	 * @param ids
	 *            archiveno_format表的entity_id 字段
	 * @param tableNames 文档相对应的表
	 * @return
	 */
	public boolean copySysTable(ClassEntityRelation entityRelation, String ids, String tableNames) {
		List<HashMap<String, Object>> values = classEntityRelationDao
				.getAllParent(entityRelation.getpId());
		copyTable("class_entity_relation", values);
		if(tableNames == null){
			values = listViewDao.getAllByTableName(entityRelation.geteTableName());
		}else{
			values = listViewDao.getAllByTableName(tableNames);
		}
		copyTable("table_interface_list_view", values);

		// 子节点没有存储规则的情况没有处理,(已处理，2016-2-16 16:54:53 cdg
		if (null == ids || "".equals(ids)) {
			values = formatDao.getAllByEntityId(entityRelation.getcEType());
		} else {
			values = formatDao.getAllByEntityIds(ids);
		}
		copyTable("archiveno_format", values);
		
		//cdg 2016年3月30日 add
//		String selecetSQL = "select * from   where ";
//		values = baseDao.executeSelecetSQL(selecetSQL);
//		copyTable("table_interface_list_view", values);
		// end add
		System.out.println("----------------copySysTable end --");
		return true;
	}

	/**
	 * 在复制表的时候复制原文
	 * 
	 * @param progressFrame
	 * 
	 * @param tableName
	 * @param fileValueMap
	 *            发布的表数据，有主键（recid）字段
	 * @param newPath
	 *            复制新路径
	 * @param selectParas
	 * @param futures
	 * @param executorService
	 * @return
	 */
	// TODO 以后修改sql 语句
	public boolean copyIsFilesTableAndFiles(ProgressFrame progressFrame,
			Map<String, String> fileValueMap, String osPosition,
			String newPath, List<SelectParameters> selectParas,
			String... tableNames) {
		if (null == tableNames || null == fileValueMap || null == newPath)
			return false;
		// cdg 2016-3-15 14:59:33
		int cpuCore = Runtime.getRuntime().availableProcessors();
		int nThread = 20 * cpuCore + 1;
		ExecutorService executorService = Executors.newFixedThreadPool(nThread);
		List<CompletableFuture<Boolean>> futures = new ArrayList<CompletableFuture<Boolean>>(
				nThread);
		// end
		try {
			Page page = new Page(1);
			filesDao.getFilesByRecidByPage(page, selectParas, tableNames);
			List<TableFiled> fileds = createTable("is_files");
			long totalNumber = page.getTotalNumber();

			if (totalNumber <= 5000)
				page.setPageNumberNotCount(totalNumber);
			else
				page.setPageNumberNotCount(5000);
			
			int index = progressFrame.addOneCheck("复制原文", "0/" + totalNumber);
			int idex = progressFrame.addOneCheck("复制is_files表", "0/" + totalNumber);
			while (true) {
				List<HashMap<String, Object>> values = filesDao
						.getFilesByRecidByPage(page, selectParas, tableNames);

				if (null != values) {
					values.forEach(m -> {
						String recid = (String) m.get("rec_id");
						String newLocPath = fileValueMap.get(recid);
						String des_path[] = new String[2];
						if (null == osPosition || "".equals(osPosition)) {
							des_path[0] = (String) m.get("os_position")
									+ (String) m.get("file_loc")
									+ (String) m.get("file_name");
						} else {
							des_path[0] = osPosition
									+ (String) m.get("file_loc")
									+ (String) m.get("file_name");
						}
						des_path[1] = (String) m.get("os_position")
								+ (String) m.get("file_loc")
								+ (String) m.get("file_id")
								+ (String) m.get("file_type");
						futures.add(CompletableFuture.supplyAsync(
								() -> copyFiles(progressFrame, totalNumber,
										index, newPath + newLocPath, des_path),
								executorService));

						// 替换新路径
						m.put("os_position", newPath);
						m.put("file_loc", newLocPath);
					});
				}
				// 写入数据
				if (null != values && values.size() > 0) {
					MultipleDataSource.setDialect(MultipleDataSource.SQLITE);
					baseDaoSqlite.insert("is_files", MultipleDataSource
							.getDialect().getInsertSql(fileds, values));
				}
				progressFrame.updateProgressCheck((page.getDbIndex()+page.getDbNumber())+"/"+totalNumber, progressFrame.getRowCheck(idex), 1);
				if (page.getCurrentPage() >= page.getTotalPage()) {
					break;
				}
				page.setCurrentPage(page.getCurrentPage() + 1);
			}
			progressFrame.removeOneCheck(progressFrame.getRowCheck(idex));
			futures.forEach(f -> {
				try {
					f.get();
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
			progressFrame.removeOneCheck(progressFrame.getRowCheck(index));
		} finally {
			executorService.shutdown();
		}
		System.out.println("-----------copyIsFilesTableAndFiles end");
		return true;
	}

	protected boolean copyFiles(ProgressFrame progressFrame, long totalNumber,
			int index, String tar_path, String... des_path) {
		int count = 0;
		while (count < 2) {
			// 复制文件
			if(FileUtil.copyFileOne(des_path[count], tar_path) == 1){
				break;
			}
			count++;
		}
		if (count >= 2) {
			// TODO 文件不存在
			return false;
		}
		progressFrame.updateProgressCheck(getV(totalNumber),
				progressFrame.getRowCheck(index), 1);
		return true;
	}

	private int count = 1;

	public synchronized String getV(long totalNumber) {
		return count++ + "/" + totalNumber;
	}

	/**
	 * 复制表结构和数据
	 * 
	 * @param tableName
	 * @param values
	 * @return
	 */
	@PushLog
	public boolean copyTable(String tableName,
			List<HashMap<String, Object>> values) {
		List<TableFiled> fileds = createTable(tableName);
		if (null != values && values.size() > 0)
			baseDaoSqlite.insert(tableName, MultipleDataSource.getDialect()
					.getInsertSql(fileds, values));
		System.out.println("-------------copy" + tableName + "   end--");
		return true;
	}

	/**
	 * 复制表结构
	 * 
	 * @param tableName
	 * @return 表的字段属性（名字，类型）
	 */
	public List<TableFiled> createTable(String tableName) {
		List<TableFiled> fileds = new ArrayList<TableFiled>();
		// List<String> primaryKeys = baseDao.getPrimaryKey(tableName);
		List<HashMap<String, Object>> rs = baseDao
				.getFiledType(tableName, null);
		if (baseDaoSqlite.isExistsTable(tableName) > 0)
			baseDaoSqlite.dropeTable(tableName);
		Map<String, Object> map = null;
		TableFiled filed = null;

		for (int i = 0, length = rs.size(); i < length; i++) {
			map = rs.get(i);
			filed = new TableFiled((String) map.get(MySqlProvider.FILED_NAME),
					MultipleDataSource.getDialect().changeType(
							(String) map.get(MySqlProvider.FILED_TYPE)));
			// TODO 没有复制主外键等，只复制数据
			fileds.add(filed);
		}
		baseDaoSqlite.createTable(tableName, fileds);
		return fileds;
	}

	@Override
	public Boolean pushDatas(ProgressFrame progressFrame,
			List<SelectParameters> selectParas, CheckNode checkNode,
			boolean isCopyFiles, String osPosition, String newPath) {
		ClassEntityRelation cer = (ClassEntityRelation) checkNode
				.getUserObject();
		if (cer.geteTableName() == null || "".equals(cer.geteTableName()))
			return false;

		System.out.println("pushData");

		boolean fog = true;
		// 2016年3月4日10:18:03
		if (checkNode.isLeaf()) {
			CheckNode node = (CheckNode) checkNode.getParent();
			cer = (ClassEntityRelation) node.getUserObject();
			if (cer.geteTableName() == null || "".equals(cer.geteTableName())) {
				// 单层结构
				fog = false;
				ClassEntityRelationWithArchive c = (ClassEntityRelationWithArchive) checkNode
						.getUserObject();
				pushDataOne(progressFrame, c, selectParas, cer.getcEType(),
						isCopyFiles, osPosition, newPath);
			} else {
				cer = (ClassEntityRelation) checkNode.getUserObject();
			}
		}
		if (fog) {
			// 多层结构
			// 先创建带有筛选条件的表的数据，然后创建和这个表相关的其他的表数据
			List<HashMap<String, Object>> values = baseDao.getAllWithParas(
					cer.geteTableName(), selectParas);
			int index = progressFrame.addOneCheck(cer.getItemName(), "正在复制");
			// 复制选中表
			copyTable(cer.geteTableName(), values);
			progressFrame.updateProgressCheck("复制完成",
					progressFrame.getRowCheck(index), 1);
			progressFrame.removeOneCheck(index);

			CheckNode node = checkNode;
			// 取得存储规则的字段（已排序）
			String[] format = ((ClassEntityRelationWithArchive) node
					.getUserObject()).getFormat()[2];
			node = (CheckNode) checkNode.getFirstLeaf();
			String notCopyTable = cer.geteTableName();
			cer = (ClassEntityRelation) node.getUserObject();

			StringBuilder ids = new StringBuilder();
			String cEType = cer.getcEType();
			// String tableName = null;
			StringBuilder whereAnd = new StringBuilder();
			selectParas.forEach(v -> whereAnd.append(" and "
					+ MySqlProvider.solveParameters((SelectParameters) v)));
			String tableName = null;
			StringBuilder tableNames = new StringBuilder();
			while (cEType != null && !"".equals(cEType)) {
				ids.append(", '" + cEType + "'");
				tableName = cer.geteTableName() == null ? "" : cer
						.geteTableName().trim();
				if (null != tableName && !"".equals(tableName)) {
					tableNames.append(",'"+tableName+"'");
					if(!tableName.equals(notCopyTable)){
						values = baseDao
								.executeSelecetSQL("SELECT * FROM ( SELECT folder_no FROM "
										+ notCopyTable
										+ " WHERE 1=1 "
										+ whereAnd
										+ " group by folder_no ) as t,"
										+ tableName
										+ " as t1 WHERE t.folder_no = t1.folder_no");
						index = progressFrame.addOneCheck(tableName, "正在复制");
						copyTable(tableName, values);
						progressFrame.updateProgressCheck("复制完成",
								progressFrame.getRowCheck(index), 1);
						progressFrame.removeOneCheck(index);
					}
				}
				if (node.isLeaf() && isCopyFiles) {
					if (tableName.equals(notCopyTable))
						copyIsFilesTableAndFiles(progressFrame,
								getRceidAndRule(values, format), osPosition,
								newPath, selectParas, tableName);
					else
						copyIsFilesTableAndFiles(progressFrame,
								getRceidAndRule(values, format), osPosition,
								newPath, selectParas, tableName, notCopyTable);
				}
				node = (CheckNode) node.getParent();
				cer = (ClassEntityRelation) node.getUserObject();
				cEType = cer.getcEType();
			}
			if (ids.length() >= 0)
				ids.deleteCharAt(0);
			if(tableNames.length()>=0)
				tableNames.deleteCharAt(0);
			// 复制相关系统表
			// 只需要叶子节点的pid则能查出所有父节点的数据
			index = progressFrame.addOneCheck("系统表", "正在发布");
			copySysTable((ClassEntityRelation) checkNode.getUserObject(),
					ids.toString(),tableNames.toString());
			progressFrame.updateProgressCheck("完成",
					progressFrame.getRowCheck(index), 1);
			progressFrame.removeOneCheck(index);
		}
		// 复制sqlite数据库文件, cdg 2016-2-24 11:20:01
//		int index = progressFrame.addOneCheck("数据库", "正在发布");
//		copySqliteDB(newPath);
//		progressFrame.updateProgressCheck("完成",
//				progressFrame.getRowCheck(index), 1);
//		progressFrame.removeOneCheck(index);
		return true;
	}

	/**
	 * 复制sqlite数据库
	 * 
	 * @author cdg 2016-2-24 11:20:30
	 * @param path
	 * @return
	 */
	protected boolean copySqliteDB(String path) {
		if(	FileUtil.copyFileOne(MyConstants.getSqliteDBPath(), path+ File.separator)==1){
			return true;
		}
		return false;
	}

	/**
	 * 取得recid主键值已经对应的档号规则
	 * 
	 * @param values
	 * @param format
	 * @return key为保存的recid value为对应的档号规则的文件存储路径 a/d/v/d
	 */
	protected Map<String, String> getRceidAndRule(
			List<HashMap<String, Object>> values, String[] format) {
		// 取得发布数据表里的rec_id字段值，以便复制is_files表
		StringBuilder locPath = new StringBuilder();
		// StringBuilder recids = new StringBuilder();
		Map<String, String> fileValueMap = new HashMap<String, String>();
		values.forEach(map -> {
			for (int i = 0; i < format.length; i++) {
				locPath.append(File.separator + map.get(format[i]));
			}
			locPath.append(File.separator);
			fileValueMap.put((String) map.get("recid"), locPath.toString());
			// recids.append(",'" + map.get("recid") + "' ");
			locPath.delete(0, locPath.length());
		});
		// recids.deleteCharAt(0);
		// fileValueMap.put("recIds", recids.toString());
		return fileValueMap;
	}

}
